import struct
import csv
import os
import sys
import glob
import numpy as np

def parse_bin_file_optimized(bin_file_path, csv_file_path):
    """
    参数:
    bin_file_path: BIN文件路径
    csv_file_path: 输出CSV文件路径
    """
    cur_file_obj_frames = int(sys.argv[2])
    cur_file_frame_cnt = 0

    # 检查文件是否存在
    if not os.path.exists(bin_file_path):
        print(f"错误: 文件 {bin_file_path} 不存在")
        return

    file_size = os.path.getsize(bin_file_path) # 文件大小
    file_needed = 8384  # 每一个文件处理8384字节
    file_count = file_size // file_needed
    file_index = 0  # 文件序号

    # 从bin文件读取指定量数据并进行处理
    with open(bin_file_path, 'rb') as f:
        for i in range(file_count):
            # f.seek(i * file_needed)
            data = f.read(file_needed)
            if len(data) != file_needed:
                print("数据量不够，程序退出")

            if file_size <= file_needed*cur_file_obj_frames:
                process_bin_data(data, csv_file_path, cur_file_frame_cnt)#一个文件可以存完所有数据
                cur_file_frame_cnt += 1
            else:
                mew_csv_file = csv_file_path[:-4] + '_' + str(file_index) + ".csv"
                process_bin_data(data, mew_csv_file, cur_file_frame_cnt)
                cur_file_frame_cnt += 1
                if cur_file_frame_cnt >= cur_file_obj_frames:
                    cur_file_frame_cnt = 0
                    file_index += 1

def process_bin_data(data, csv_file_path, cur_file_frame_cnt):
    """
    处理BIN文件数据并生成CSV文件
    参数:
    data: BIN文件的二进制数据
    csv_file_path: 输出CSV文件路径
    """

    # 计算总帧数
    frame_size = 64  # 每帧64字节
    total_frames = len(data) // frame_size

    if total_frames == 0:
        print("文件太小，不足一帧数据")
        return

    # 读取第一帧获取数据包有效长度
    first_frame = data[0:frame_size]
    packet_data_length = struct.unpack('<H', first_frame[1:3])[0]  # 第2、3字节，小端序

    # 总帧数就是有效帧数
    total_valid_frames = total_frames

    # 使用numpy优化：直接从文件中提取前8192字节作为通道数据
    channel_data = bytearray()

    # 从所有帧中提取有效数据部分
    for i in range(total_frames):
        start_idx = i * frame_size
        end_idx = start_idx + frame_size
        frame_data = data[start_idx:end_idx]

        # 根据帧的类型提取有效数据
        if i == 0:
            # 第一帧：第4-64字节是有效数据（前3字节是帧序号和长度信息）
            valid_bytes = frame_data[3:]
        else:
            # 其余帧：第2-64字节是有效数据（前1字节是帧序号）
            valid_bytes = frame_data[1:]

        channel_data.extend(valid_bytes)

    # 确保只取前8192字节作为通道数据
    channel_data = bytes(channel_data[:8192])

    # 检查是否有足够的数据
    if len(channel_data) < 8192:
        print(f"警告: 通道数据不足8192字节，只有{len(channel_data)}字节")

    # 使用numpy优化：将8192字节按通道分配（每个通道1024字节）
    channel_array = np.frombuffer(channel_data, dtype=np.uint8)
    byte_channels = np.reshape(channel_array, (8, 1024))

    # 使用numpy优化：将字节数据转换为点数据（每两个字节组成一个点）
    point_channels = []
    for channel_bytes in byte_channels:
        # 使用numpy直接转换字节为16位无符号整数，小端序
        points = np.frombuffer(channel_bytes, dtype=np.uint16)
        point_channels.append(points.tolist())

    # 提取附加信息（从8192字节之后的52字节）
    additional_info_data = bytearray()
    # 从所有帧中收集剩余的数据作为附加信息
    for i in range(total_frames):
        start_idx = i * frame_size
        end_idx = start_idx + frame_size
        frame_data = data[start_idx:end_idx]

        # 根据帧的类型提取有效数据
        if i == 0:
            # 第一帧：第4-64字节是有效数据
            valid_bytes = frame_data[3:]
        else:
            # 其余帧：第2-64字节是有效数据
            valid_bytes = frame_data[1:]

        additional_info_data.extend(valid_bytes)

    # 附加信息在8192字节之后
    additional_info_data = bytes(additional_info_data[8192:8192+52]) if len(additional_info_data) >= 8192+52 else b''

    # 解析附加信息
    additional_info_items = []
    if len(additional_info_data) >= 52:
        try:
            offset = 0
            additional_info_items.append(str(additional_info_data[offset]))  # vehicletype
            offset += 1
            additional_info_items.append(str(additional_info_data[offset]))  # vehtotalmileage
            offset += 1
            additional_info_items.append(str(struct.unpack('<H', additional_info_data[offset:offset+2])[0]))  # vehspd
            offset += 2
            additional_info_items.append(str(additional_info_data[offset]))  # rainlevel
            offset += 1
            additional_info_items.append(str(additional_info_data[offset]))  # guard
            offset += 1
            additional_info_items.append(str(additional_info_data[offset]))  # reserved
            offset += 1
            additional_info_items.append(str(additional_info_data[offset]))  # year
            offset += 1
            additional_info_items.append(str(additional_info_data[offset]))  # month
            offset += 1
            additional_info_items.append(str(additional_info_data[offset]))  # date
            offset += 1
            additional_info_items.append(str(additional_info_data[offset]))  # hour
            offset += 1
            additional_info_items.append(str(additional_info_data[offset]))  # min
            offset += 1
            additional_info_items.append(str(additional_info_data[offset]))  # sec
            offset += 1
            additional_info_items.append(str(struct.unpack('<H', additional_info_data[offset:offset+2])[0]))  # ms
            offset += 2
            additional_info_items.append(str(additional_info_data[offset]))  # carshchl
            offset += 1
            additional_info_items.append(str(additional_info_data[offset]))  # carshsts
            offset += 1

            # PonyVehicleID 用8行显示
            for i in range(8):
                additional_info_items.append(str(additional_info_data[offset+i]))
            offset += 8

            # Swversion 用3行显示
            for i in range(3):
                additional_info_items.append(chr(additional_info_data[offset+i]) if 32 <= additional_info_data[offset+i] <= 126 else '.')
            offset += 3

            # Algversion 用3行显示
            for i in range(3):
                additional_info_items.append(chr(additional_info_data[offset+i]) if 32 <= additional_info_data[offset+i] <= 126 else '.')
            offset += 3

            additional_info_items.append(str(additional_info_data[offset]))  # communication_err
            offset += 1
            additional_info_items.append(str(additional_info_data[offset]))  # systerm_err
            offset += 1
            additional_info_items.append(str(additional_info_data[offset]))  # config_err
            offset += 1
            additional_info_items.append(str(struct.unpack('<H', additional_info_data[offset:offset+2])[0]))  # chl0crashstrength
            offset += 2
            additional_info_items.append(str(struct.unpack('<H', additional_info_data[offset:offset+2])[0]))  # chl1crashstrength
            offset += 2
            additional_info_items.append(str(struct.unpack('<H', additional_info_data[offset:offset+2])[0]))  # chl2crashstrength
            offset += 2
            additional_info_items.append(str(struct.unpack('<H', additional_info_data[offset:offset+2])[0]))  # chl3crashstrength
            offset += 2
            additional_info_items.append(str(struct.unpack('<H', additional_info_data[offset:offset+2])[0]))  # chl4crashstrength
            offset += 2
            additional_info_items.append(str(struct.unpack('<H', additional_info_data[offset:offset+2])[0]))  # chl5crashstrength
            offset += 2
            additional_info_items.append(str(struct.unpack('<H', additional_info_data[offset:offset+2])[0]))  # chl6crashstrength
            offset += 2
            additional_info_items.append(str(struct.unpack('<H', additional_info_data[offset:offset+2])[0]))  # ch17crashstrength
            offset += 2
            additional_info_items.append(str(struct.unpack('<H', additional_info_data[offset:offset+2])[0]))  # capvalue
        except Exception as e:
            print(f"解析附加信息时出错: {e}")
            # 如果解析失败，填充空值
            additional_info_items = [''] * 50  # 预估需要的行数
    else:
        print("附加信息不完整，无法解析")
        # 如果解析失败，填充空值
        additional_info_items = [''] * 50  # 预估需要的行数

    # 写入CSV文件
    try:
        # 根据cur_file_frame_cnt决定是创建新文件还是追加到文件
        if cur_file_frame_cnt == 0:
            file_mode = 'w'
        else:
            file_mode = 'a'
        with open(csv_file_path, file_mode, newline='', encoding='utf-8') as csvfile:
            writer = csv.writer(csvfile)

            # 只有在创建新文件时才写入表头
            if cur_file_frame_cnt == 0:
                # 写入表头（8个通道 + 1列附加信息值）
                header = [f'Channel_{i+1}' for i in range(8)] + ['Item_Value']
                writer.writerow(header)

            # 写入数据行（每个通道512个点）
            max_info_items = len(additional_info_items)

            for i in range(512):
                row = []
                # 添加8个通道的数据
                for j in range(8):
                    if i < len(point_channels[j]):
                        row.append(point_channels[j][i])
                    else:
                        row.append('')

                # 添加附加信息值（第9列）
                if i < max_info_items:
                    row.append(additional_info_items[i])  # 成员值
                else:
                    row.append('0')  # 填充0而不是空值
                writer.writerow(row)

        #print(f"CSV文件已保存至: {csv_file_path}")
    except PermissionError:
        print(f"错误: 没有权限写入文件 {csv_file_path}")
        print("请检查文件是否被其他程序占用")
    except Exception as e:
        print(f"写入CSV文件时出错: {e}")

def process_all_bin_files(folder_path):
    """
    处理指定文件夹下的所有bin文件

    参数:
    folder_path: 包含bin文件的文件夹路径
    """
    # 确保文件夹路径存在
    if not os.path.exists(folder_path):
        print(f"错误: 文件夹 {folder_path} 不存在")
        return

    # 查找文件夹下所有的bin文件
    bin_files_pattern = os.path.join(folder_path, "*.bin")
    bin_files = glob.glob(bin_files_pattern)

    if not bin_files:
        print(f"在文件夹 {folder_path} 中未找到任何bin文件")
        return

    print(f"找到 {len(bin_files)} 个bin文件")

    # 处理每个bin文件
    for bin_file_path in bin_files:
        try:
            #print(f"\n正在处理文件: {bin_file_path}")

            # 生成对应的CSV文件路径（将.bin替换为.csv）
            csv_file_path = bin_file_path[:-4] + ".csv"

            # 调用解析函数
            parse_bin_file_optimized(bin_file_path, csv_file_path)

        except Exception as e:
            print(f"处理文件 {bin_file_path} 时出错: {e}")

    print(f"\n所有文件处理完成！")


# 使用示例
if __name__ == "__main__":
    # 检查命令行参数
    if len(sys.argv) != 3:
        sys.exit(1)
    param_path = sys.argv[1]
    # 根据文件夹或者bin文件做相应的处理
    if param_path.endswith('.bin'):
        csv_file_path = param_path[:-4] + ".csv"
        parse_bin_file_optimized(param_path, csv_file_path)
    else:
        process_all_bin_files(param_path)
